﻿using System;
using System.Net;

using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Tulde.Windows.Music;
using System.IO;
using System.Windows;
using Microsoft.Phone.Shell;


namespace Tulde.WindowsPhone.Music.Mp3Tunes
{
    public abstract class BaseDataService<T> : IDataService<T>
    {
        protected abstract string BaseUrl { get; }


        #region IDataService<T> Members

        public virtual Uri CreateServiceEndpoint(T operation, ServiceParameters parameters)
        {
            return new Uri(this.AppendParametersToUrl(this.BaseUrl, operation.ToString(), parameters), UriKind.Absolute);
        }
        public virtual void InvokeAsynch<T1>(Uri uri, Action<T1> callback)
        {
            //in case this operation takes longer than the screen timeout, we need to disable
            PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
            {
                if (ViewModelCacheFactory.Instance.CacheItemExists(uri.OriginalString))
                {
                    callback(ViewModelCacheFactory.Instance.GetCacheItem<T1>(uri.OriginalString));
                }
                else
                {
                    System.Net.WebClient authClient = new WebClient();
                    authClient.DownloadStringCompleted += (sender, e) =>
                        {
                            if (e.UserState != null)
                            {
                                string result = String.Empty;
                                try
                                {
                                    result = e.Result;
                                }
                                catch (WebException ex)
                                {
                                    MessageBox.Show("Unable to establish a network connection", ex.Message, MessageBoxButton.OK);

                                }
                                T1 instance = (T1)this.DeserializeResult(result);
                                DataTransportPair state = e.UserState as DataTransportPair;
                                //restore screen timeouts.
                                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Enabled;
           
                                callback(instance);

                                if (instance is ICacheable)
                                {
                                    if (((ICacheable)instance).ShouldCache)
                                    {
                                        ViewModelCacheFactory.Instance.SetCacheItem(uri.OriginalString, instance);
                                    }
                                }




                            }
                        };

                    authClient.DownloadStringAsync(uri, new DataTransportPair { Callback = callback, Uri = uri });

                }
            }
            else
            {
                MessageBox.Show("Unable to establish a network connection", "Network Error", MessageBoxButton.OK);
            }
        }
        public virtual void ReadBufferAudioStream(Uri uri, Action<byte[]> callback)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

            request.AllowReadStreamBuffering = false;
            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(ReadAsynch), new List<object> { callback, request });

        }
        protected virtual void ReadAsynch(IAsyncResult res)
        {
            List<object> state = res.AsyncState as List<object>;


            Action<byte[]> callback = state[0] as Action<byte[]>;
            HttpWebRequest request = (HttpWebRequest)state[1];
            HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(res);
            Stream r = response.GetResponseStream();

            byte[] data = new byte[4096];
            int read;

            while ((read = r.Read(data, 0, data.Length)) > 0)
            {
                if (callback != null)
                    callback(data);
            }



        }
        protected abstract object DeserializeResult(string result);

        protected virtual void DataService_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.UserState != null)
            {
                string result = String.Empty;
                try
                {
                    result = e.Result;
                }
                catch (WebException ex)
                {
                    MessageBox.Show("Unable to establish a network connection", ex.Message, MessageBoxButton.OK);

                }
                object instance = this.DeserializeResult(result);
                DataTransportPair state = e.UserState as DataTransportPair;
                object action = state.Callback;
                Type type = action.GetType();
                var method = type.GetMethod("Invoke");
                method.Invoke(action, new object[] { instance });

                Uri uri = state.Uri as Uri;
                if (instance is ICacheable)
                {
                    if (((ICacheable)instance).ShouldCache)
                    {
                        ViewModelCacheFactory.Instance.SetCacheItem(uri.OriginalString, instance);
                    }
                }




            }
        }

        protected virtual string AppendParametersToUrl(string url, string operation, ServiceParameters parameters)
        {
            List<string> parametersList = new List<string>();
            foreach (string key in parameters.Keys)
            {
                parametersList.Add(String.Format("{0}={1}", key, parameters.GetParameter(key)));
            }
            if (parameters == null || !parameters.Keys.Any())
            {
                return url;
            }
            else
            {
                return url + operation + "?" + String.Join("&", parametersList.ToArray());
            }
        }

        #endregion
    }

    internal class DataTransportPair
    {
        public object Callback { get; set; }
        public object Uri { get; set; }
    }
}
